/**
 * Copyright (c) 2016-2020 https://github.com/zhaohuatai
 *
 * contact 824069438@qq.com
 *  
 */
package org.zfes.snowy.auth.shiro.filter.authc;

import java.util.Collection;
import java.util.Optional;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.zfes.snowy.auth.AuthConsts;
import org.zfes.snowy.auth.shiro.event.AuthCacheClearType;
import org.zfes.snowy.auth.shiro.event.AuthChangeEvent;
import org.zfes.snowy.auth.shiro.exceptions.IncorrectCaptchaException;
import org.zfes.snowy.auth.shiro.filter.MultiLoginLimitUtil;
import org.zfes.snowy.auth.shiro.suser.SessionUser;
import org.zfes.snowy.auth.shiro.suser.SessionUserManager;
import org.zfes.snowy.core.util.AppCtxUtil;
import org.zfes.snowy.core.util.ZStrUtil;

public  class SnowyTokenAuthenticationFilter extends SnowyAuthenticationFilter{
	  
      protected String usernameParam = AuthConsts.LOGIN.DEFAULT_USERNAME_PARAM;
      protected String passwordParam =AuthConsts.LOGIN.DEFAULT_PASSWORD_PARAM;
      protected String rememberMeParam = AuthConsts.LOGIN.DEFAULT_REMEMBER_ME_PARAM;
      protected String captchaParam = AuthConsts.LOGIN.DEFAULT_CAPTCHA_PARAM;
      protected String loginOrginParam = AuthConsts.LOGIN.DEFAULT_LOGINORGIN_PARAM;
      
     	@Autowired  
  		private  ApplicationEventPublisher publisher;
     	@Autowired
		private SessionUserManager sessionUserManager;
//代价太高，，此处不用次函数拦截
      //  	@Override
//    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
//        return super.isAccessAllowed(request, response, mappedValue) && (!isLoginSubmission(request, response) );
//    }
	   	@Override
	   	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
	   		if(isLoginPortUrlRequest( request,  response)){
	        	return true;
	        }
	   		if(isLoginSubmission( request,  response)) {
	   			return executeLogin( request,  response) ;
	   		}
//	   		if(isEnableSnowySSOClient){//是否支持sso
//	   			if(isSSOCallBack( request,  response )){
//	   				return onSSOServerRedirectBack( request,  response);
//	   			}
//	   			return doSSOSync(request,  response);
//	   		}else{
//	   			authecUnAuthecHandler.onAuthenticationUnAuthec(WebUtils.toHttp(request), WebUtils.toHttp(response));
//	   		}
	   		authecUnAuthecHandler.onAuthenticationUnAuthec(WebUtils.toHttp(request), WebUtils.toHttp(response));
	   		return false;
	   	}
	
	   	@Override
		public void syncClientCache(Session session,String appKey){
			Long serverAuthorCacheVersion=(Long) session.getAttribute(appKey+"_acv");//activeCacheVersion
			if(serverAuthorCacheVersion!=null&&clientAuthorizationCacheVersion<serverAuthorCacheVersion){
				this.publisher.publishEvent(new AuthChangeEvent(SecurityUtils.getSubject(),AuthCacheClearType.cachedAuthorizationInfoBySubject));  
				clientAuthorizationCacheVersion=serverAuthorCacheVersion;
			}
		}
	   	
	    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
	        AuthenticationToken token = createToken(request, response);
	        if (token == null) {
	            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken must be created in order to execute a login attempt.";
	            throw new IllegalStateException(msg);
	        }
	      //登录终端类型
            if(!supportsLoginOrgins.contains(getLoginOrgin(request))){
            	authecFailureHandler.onAuthenticationFailure(WebUtils.toHttp(request), WebUtils.toHttp(response), new AuthenticationException("登录终端类型参数错误") );
            	return false;
            }
	        try {
	            Subject subject = getSubject(request, response);
//	            if(subject.isAuthenticated()) {
//		        	   try {
//		        		   subject.logout();
//		        	   }catch (Exception e) {
//						
//					}
//		           }
	            Session session=subject.getSession();
	            
	            doCaptchaValidate( request, session,  token);
	            
	            subject.login(token);
	            	
	           
	            //登录后，初始化session_user信息 
	        	SessionUserManager sessionUserManager=AppCtxUtil.getBean("sessionUserManagerImpl");
	        	Optional<SessionUser> sessionUserOp =sessionUserManager.loadSessionUser((String)subject.getPrincipal());
				if(!sessionUserOp.isPresent()){
					 throw new AuthenticationException("未正确初始化用户信息");
				}
				SessionUser sessionUser=sessionUserOp.get();
				sessionUser.setAuthLevel(2);
				session.setAttribute(AuthConsts.SESSION.SESSION_USER_KEY,sessionUser);
				session.setAttribute(AuthConsts.SESSION.SESSION_LOGINORGIN_KEY,getLoginOrgin(request));
	            if(isEnableSessionStrategy&&maxSession!=null&&maxSession>0){
	            	MultiLoginLimitUtil.validateSessionLimit(subject, maxSession);
	            }
	            
	            authecSuccessHandler.onAuthenticationSuccess(WebUtils.toHttp(request), WebUtils.toHttp(response), subject,getIsEnableCSRFToken());
	        } catch (AuthenticationException exception) {
	        	authecFailureHandler.onAuthenticationFailure(WebUtils.toHttp(request), WebUtils.toHttp(response), exception);
	        }
	        return false;
	    }  
	    
	    protected  void doCaptchaValidate(ServletRequest request,Session session, AuthenticationToken token) {
	    	String captchaFromSubmit=WebUtils.getCleanParam(request, getCaptchaParam());
	    	System.out.println("SnowyTokenAuthenticationFilter--sessionid:---||"+session.getId());
	    	 Collection<Object> sd= session.getAttributeKeys();
		        sd.forEach(val->{System.out.println("SnowyCapchaProducerFilter--Session----sessionid:---AttributeKeys||"+val);});
		        
	        String captchaFromSession =(String) session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
	        if (ZStrUtil.hasNoText(captchaFromSubmit)||!ZStrUtil.equals(captchaFromSubmit, captchaFromSession)) {
	            throw new IncorrectCaptchaException("验证码错误！");
	        }else{
	        	session.removeAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
	        }
	       
        }
	  //----------------------------------------createToken----------------------------------------------------------------------------
      protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
          String username = getUsername(request);
          String password = getPassword(request);
          return createToken(username, password, request, response);
      }
      
      protected AuthenticationToken createToken(String username, String password,ServletRequest request, ServletResponse response) {
  			boolean rememberMe =isRememberMe(request);
  			String host = getHost(request);
  			return createToken(username, password, rememberMe, host);
  	   }
      protected AuthenticationToken createToken(String username, String password,boolean rememberMe, String host) {
      	return new UsernamePasswordToken(username, password, rememberMe, host);
      }
      
 //------------------------------------------------------------------------------------------------------------     
      
      protected String getUsername(ServletRequest request) {
          return WebUtils.getCleanParam(request, getUsernameParam());
      }

      protected String getPassword(ServletRequest request) {
          return WebUtils.getCleanParam(request, getPasswordParam());
      }

      protected boolean isRememberMe(ServletRequest request) {
          return WebUtils.isTrue(request, getRememberMeParam());
      }
      protected String getHost(ServletRequest request) {
          return request.getRemoteHost();
      }
      protected String getCaptcha(ServletRequest request) {
          return WebUtils.getCleanParam(request, getCaptchaParam());
      }
      protected String getLoginOrgin(ServletRequest request) {
          return WebUtils.getCleanParam(request, getLoginOrginParam() );
      }
      public String getUsernameParam() {
          return usernameParam;
      }
  	 public void setUsernameParam(String usernameParam) {
          this.usernameParam = usernameParam;
      }

      public String getPasswordParam() {
          return passwordParam;
      }
      public void setPasswordParam(String passwordParam) {
          this.passwordParam = passwordParam;
      }

      public String getRememberMeParam() {
          return rememberMeParam;
      }

      public void setRememberMeParam(String rememberMeParam) {
          this.rememberMeParam = rememberMeParam;
      }
	public String getCaptchaParam() {
		return captchaParam;
	}
	public void setCaptchaParam(String captchaParam) {
		this.captchaParam = captchaParam;
	}


	public String getLoginOrginParam() {
		return loginOrginParam;
	}


	public void setLoginOrginParam(String loginOrginParam) {
		this.loginOrginParam = loginOrginParam;
	}

	public ApplicationEventPublisher getPublisher() {
		return publisher;
	}

	public void setPublisher(ApplicationEventPublisher publisher) {
		this.publisher = publisher;
	}

	public SessionUserManager getSessionUserManager() {
		return sessionUserManager;
	}

	public void setSessionUserManager(SessionUserManager sessionUserManager) {
		this.sessionUserManager = sessionUserManager;
	}

}
